/*
*	目的：这段代码是一个基于UDP多播的客户端程序，主要功能是通过多线程发送多个频道的数据（包括频道列表和音乐数据）。
*		  它使用了UDP套接字、线程处理、内存分配、数据结构等技术。
*	程序流程：
*	 	1、获取频道列表：
*			通过调用 mylib_get_chn_list() 获取所有频道信息，包括频道号和描述
*		2、内存分配和数据填充：
*			为每个频道分配内存并填充其频道号和描述信息。
*			计算整个频道列表的总长度并为其分配内存。
*		3、创建多线程：
*			创建一个线程（send_list_handler）负责定期发送频道列表
*			为每个频道创建一个线程（send_data_handler），每个线程负责定期读取并发送该频道的音乐数据
*注意：send_list_handler 是发送频道列表 send_data_handler 是发送频道的音乐数据					
*
*		4、使用UDP多播发送数据：
*			 使用UDP套接字和多播组地址（GROUP_ADDR）发送数据
*			 每个线程都通过 sendto()发送数据到指定的多播组
*		5、主要结构体和函数：
*			mlib_st是一个结构体数组，包含每个频道的基本信息（频道号和描述）
*			list_entry用于存储单个频道的频道号、描述及其长度
*			chn_list_st存储所有频道的信息（一个频道列表），包括一个指向list_entry数组的指针
*			chn_data_st存储一个频道的音乐数据，包含该频道的chnid 和实际的音乐数据
*			mylib_get_chn_list：这个是一个外部函数（没有在代码中定义），他返回所有频道的信息（包含 频道号 和 描述）
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <glob.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
#include <pthread.h>

#include "netradio_proto.h"
#include "mlib.h"

#define STRSIZE		512
int lenth;


void *send_list_handler(void *s);
void *send_data_handler(void *s);
int main(void)
{
	struct mlib_st *mylib = NULL;
	int chns;
	int i;
/*
	调用函数获得每个频道的频道号和描述文件里面的内容
	mylib为struct mlib_st类型的结构体数组
	chns为频道个数
 */

	//获取频道列表（mylib是一个结构体数组包含所有频道信息  chns是频道的数量）
	mylib_get_chn_list(&miylb, &chns);

//
/*单个频道的频道号和内容*/

	struct list_entry *chn_id_des[chns];

/*整个频道的列表和内容*/
	
	struct chn_list_st *whole_chn_list = NULL;

/*给单个频道开辟空间*/

	for(i = 0; i < chns; i++) {
		chn_id_des[i] = malloc(sizeof(struct list_entry));
		if(NULL == chn_id_des[i]) {
			fprintf(stderr, "malloc chn_id_des:\n");
			return -1;
		}
	}

/*给单个频道的频道号和内容赋值*/

	//分配内存并填充频道信息
	for(i = 0; i < chns; i++) {
		chn_id_des[i]->chnid = mylib[i].chnid;
		
		chn_id_des[i] = realloc(chn_id_des[i], sizeof(struct list_entry) + strlen(mylib[i].descr) + 1);
		memcpy(chn_id_des[i]->descr, mylib[i].descr, strlen(mylib[i].descr));
		
		chn_id_des[i]->len = strlen(chn_id_des[i]->descr) + 3;
	}

/*给整个频道的结构体开辟空间*/

	//计算频道列表的总长度
	int len = 0;

	for(i = 0; i < chns; i++) { //把每个频道的结构体大小加起来
		len = len + chn_id_des[i]->len;	//
	}


//测试
	//将所有频道的信息合并到 whole_chn_list中，其中tmp 用于追踪当前位置
	whole_chn_list = malloc(len);	
	if(NULL == whole_chn_list) {
		fprintf(stderr, "malloc whole_chn_list failed:\n");
		return -1;
	}
	

/*给整个频道结构体赋值*/
	int tmp = 0;
	//将所有频道的信息合并到whole_chn_list中，其中tmp用于追踪当前位置
	whole_chn_list->chnid = CHN_LIST_ID;
	for(i = 0; i < chns; i++) {
		memcpy(((char *)whole_chn_list->entry + tmp), chn_id_des[i], chn_id_des[i]->len);
		tmp = tmp + (chn_id_des[i]->len);
	}

	lenth = len + 1;

/*音乐数据指针数组*/
	//为每个频道创建一个chn_data_st结构体，并将chnid设置为频道号
	struct chn_data_st *music_data[chns];

/*给音乐数据结构体开辟空间, 并给chnid赋值*/

	for(i = 0; i < chns; i++) {
		music_data[i] = malloc(sizeof(struct chn_data_st));
		if(NULL == music_data[i]) {
			fprintf(stderr, "malloc() music_data[] failed\n");
			return -1;
		}

		music_data[i]->chnid = i + 1;
	}
	
/* 创建线程 并发 发送频道列表和音乐数据 */

	int list_err;
	int music_err;
	pthread_t tids[chns + 1];


	//创建一个线程（send_list_handler）负责发送频道列表
	list_err = pthread_create(&tids[0], NULL, send_list_handler, whole_chn_list);
	if(list_err) {
		fprintf(stderr, "pthread_create() list failed:%s\n", strerror(list_err));
		return -1;
	}

	for(i = 0; i < chns; i++) {
		//为每个频道创建一个线程（send_data_handler）负责发送该频道的音乐数据
		music_err = pthread_create(&tids[i + 1], NULL, send_data_handler, music_data[i]);
		if(music_err) {
			fprintf(stderr, "pthread_create() music err\n");
			return -1;
		}
	}

	//等待线程结束
	for(i = 0; i < chns + 1; i++) {
		pthread_join(tids[i], NULL);
	}

	//进入死循环
	while(1) {
		;
	}

	return 0;
}


//这个函数负责发送频道列表
void *send_list_handler(void *s)
{
	int udp_socket;
	struct sockaddr_in remote_addr;
	struct ip_mreqn ipm;
	int n;
	int cnt;
	char str[STRSIZE]; 
	//struct chn_list_st *whole_chn_list = s;

	//创建UDP套接字：用于发送数据
	udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
	if(udp_socket < 0) {
		perror("socket():");
		return NULL;
	}

	inet_aton(GROUP_ADDR, &ipm.imr_multiaddr);
	inet_aton("0.0.0.0", &ipm.imr_address);
	ipm.imr_ifindex = if_nametoindex("ens33");
	if(-1 == setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_IF, &ipm, sizeof(ipm))) {
		perror("setsockopt():");
		close(udp_socket);
		return NULL;
	}

	//设置目标地址
	remote_addr.sin_family = AF_INET;
	remote_addr.sin_port = htons(RCV_PORT);
	inet_aton(GROUP_ADDR, &remote_addr.sin_addr);

	//发送频道列表
	while(1) {

		//通过 sendto 将整个频道列表发送到多播组
		sendto(udp_socket, s, lenth, 0, (void *)&remote_addr, sizeof(remote_addr));
		sleep(1);
	}

	pthread_exit(NULL);
}


//这个函数负责发送单个频道的音乐数据
void *send_data_handler(void *s)
{
	int udp_socket;
	struct sockaddr_in remote_addr;
	struct ip_mreqn ipm;
	int n;
	int cnt;
	char str[STRSIZE]; 
	//struct chn_list_st *whole_chn_list = s;
	struct chn_data_st *music_data = s;

	//创建UDP套接字
	udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
	if(udp_socket < 0) {
		perror("socket():");
		return NULL;
	}

	//配置多播
	printf("%d\n", music_data->chnid);
	inet_aton(GROUP_ADDR, &ipm.imr_multiaddr);
	inet_aton("0.0.0.0", &ipm.imr_address);
	ipm.imr_ifindex = if_nametoindex("ens33");
	if(-1 == setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_IF, &ipm, sizeof(ipm))) {
		perror("setsockopt():");
		close(udp_socket);
		return NULL;
	}


	//设置目标地址
	remote_addr.sin_family = AF_INET;
	remote_addr.sin_port = htons(RCV_PORT);
	inet_aton(GROUP_ADDR, &remote_addr.sin_addr);
	printf("%d\n",music_data->chnid);

	//读取频道数据并发送
	while(1) {
		memset(str, '\0', STRSIZE);
		//从外部库（mylib_read_chn_data）读取指定频道的音乐数据，并使用sendto将数据发送到多播组
		cnt = mylib_read_chn_data(music_data->chnid - 1, str, MSG_SIZE);
		if(cnt < 0) {
			fprintf(stderr, "mlib_read_chn_data() failed:");
			return NULL;
		}
		if(cnt == 0) {
			continue;
		}
		memset(music_data->msg, '\0', MSG_SIZE);
		//strcpy(music_data->msg, str);
		memcpy(music_data->msg, str, cnt); 
		printf("%d*******%ld cnt:%d  %ld  %ld %ld\n",music_data->chnid ,strlen(music_data->msg), cnt, sizeof(str), sizeof(*music_data), sizeof(struct chn_data_st));
		usleep(1000);
		sendto(udp_socket, music_data, sizeof(struct chn_data_st), 0, (void *)&remote_addr, sizeof(remote_addr));
	}


	pthread_exit(NULL);
}

